﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Space_Cleaner.Utils;


namespace Space_Cleaner.SpaceCleaner
{
    class SC_World
    {
        private static SC_World instance = null;
        
        private ContentManager contentMgr;
        private SC_Mouse mouse;
        private SC_Keyboard keyboard;
        private SC_Display display;
        private SC_HUD hud;
        private SC_Script script;
        
        private LinkedList<SC_SpaceObject> objects;
        private LinkedList<SC_SpaceObject> physicsObjects;
        private SC_SpaceCraft spaceCraft;
        private SC_Earth earth;
        private SC_Rocket rocket;

        public static SC_World getInstance()
        {
            if (SC_World.instance == null)
                SC_World.instance = new SC_World();
            return SC_World.instance;
        }

        private SC_World()
        {
            this.objects = new LinkedList<SC_SpaceObject>();
            this.physicsObjects = new LinkedList<SC_SpaceObject>();  
        }

        public void init(int _width, int _height, ContentManager _content)
        {
            this.contentMgr = _content;
            SC_TextureManager.getInstance().init(this.contentMgr);
            SC_TextureManager.getInstance().loadTextures();
            SC_SpaceObjectList.Initialize();
            this.display = new SC_Display(_width, _height);

            this.mouse = new SC_Mouse(contentMgr.Load<Texture2D>("curhand"));
            this.mouse.addListener(display);
            this.keyboard = new SC_Keyboard();
            
            hud = new SC_HUD(SC_TextureManager.getTexture("HUD"), SC_TextureManager.getTexture("SingleBar"));
            hud.Font = _content.Load<SpriteFont>("Courier New");

            Boolean testEnabled = false;

            if (testEnabled)
            {
                float maxDist = 800;
                earth = new SC_Earth(new Vector2(0, 0), SC_TextureManager.getTexture("earthPicture"));
                addObject(earth);

                //Add Spaceship
                spaceCraft = new SC_SpaceCraft(new Vector2(0, maxDist), SC_TextureManager.getTexture("spacecraft1"));
                spaceCraft.addForce(new Vector2(0.1f, 0));
                keyboard.addListener(spaceCraft);
                addObject(spaceCraft);
            }

            if (!testEnabled)
            {
                earth = new SC_Earth(new Vector2(0, 0), SC_TextureManager.getTexture("earthPicture"));
                addObject(earth);
            
                Random rand = new Random();
                float maxDist = 800;
                float minDist = 600;

                //Add 100 random trashs
                for (int i = 0; i < 30; i++)
                {
                    float dist = (float)rand.NextDouble() * (maxDist - minDist) + minDist;
                    float angle = 360.0f * (float)rand.NextDouble();
                    angle = Utils.degToRad(angle);

                    float x = (float)Math.Cos(angle) * dist;
                    float y = (float)Math.Sin(angle) * dist;
                    SC_Trash test = null;
                    if (rand.Next() % 2 == 0)
                        test = new SC_Trash(new Vector2(x, y), 1.0f, SC_TextureManager.getTexture("schrott1"));
                    else
                        test = new SC_Trash(new Vector2(x, y), 1.0f, SC_TextureManager.getTexture("schrott2"));
                    float forceX = (float)Math.Cos(angle - Utils.degToRad(90.0f)) * 0.4f;
                    float forceY = (float)Math.Sin(angle - Utils.degToRad(90.0f)) * 0.4f;
                    test.addForce(new Vector2(forceX, forceY));
                    addObject(test);
                }

                //Add Spaceship
                spaceCraft = new SC_SpaceCraft(new Vector2(0, maxDist), SC_TextureManager.getTexture("spacecraft1"));
                spaceCraft.addForce(new Vector2(0.1f, 0));
                keyboard.addListener(spaceCraft);
                addObject(spaceCraft);

                //ADD Rocket
                SC_Satellite satellite = new SC_Satellite(new Vector2(0.0f, 0.0f), 3.0f, contentMgr.Load<Texture2D>("satellite1"));
                addObject(satellite);
                rocket = new SC_Rocket(contentMgr.Load<Texture2D>("rocket1"));
                rocket.init(0, 700, satellite);
                addObject(rocket);

                //Add 200 Stars
                for (int i = 0; i < 200; i++)
                     addObject(new SC_Star(new Vector2((float)rand.NextDouble() * 20480 - 10240, (float)rand.NextDouble() * 13600 - 6800), SC_TextureManager.getTexture("star1"), (float)rand.NextDouble() * 0.3f, (float)rand.NextDouble()));
            }

            this.script = new SC_Script(this, this.contentMgr);
            SC_AbstractScriptEvent startEvent = new SC_StartRocketEvent(40000);
            startEvent.addListener(this.hud);
            this.script.addEvent(startEvent);
        }

        public void addObject(SC_SpaceObject _object)
        {
            objects.AddLast(_object);
            if (_object.hasPhyiscs)
                physicsObjects.AddLast(_object);
        }

        public void RemoveObject(SC_SpaceObject _object)
        {
            objects.Remove(_object);
            if (_object.hasPhyiscs)
                physicsObjects.Remove(_object);
        }

        public void draw(SpriteBatch _spriteBatch)
        {
            foreach (SC_SpaceObject obj in objects)
            {
                obj.draw(_spriteBatch, display);
                //obj.drawCollisionRadius(_spriteBatch, display);
            }
            hud.DrawHUD(_spriteBatch);
            mouse.draw(_spriteBatch);
        }

        public void update(GameTime gameTime)
        {
            display.update(gameTime.ElapsedGameTime);
            mouse.update(gameTime.ElapsedGameTime);
            keyboard.update(gameTime.ElapsedGameTime);
            if (true)
            {
                addEarthGravityAtAllObjects();
                updateAllObjects(gameTime.ElapsedGameTime);
                resetForcesOfAllObjects();
                Collision.doCollisionDetection();
                updateHud(gameTime.ElapsedGameTime);
                deleteUnusedEffects();
                updateScript(gameTime);
            }
        }

        private void updateScript(GameTime gameTime)
        {
            this.script.update(gameTime);
        }

        private void updateHud(TimeSpan elapsedTime)
        {
            hud.TrashCount = physicsObjects.Count;
            hud.AvgCol += (float)elapsedTime.TotalSeconds;
        }

        private void resetForcesOfAllObjects()
        {
            foreach (SC_SpaceObject obj in physicsObjects)
                obj.resetForce();
        }

        private void deleteUnusedEffects()
        {
            LinkedList<SC_SpaceObject> toRemove = new LinkedList<SC_SpaceObject>();

            foreach (SC_SpaceObject obj in objects)
                if (obj.GetType() == typeof(SC_Effect) && obj.Activated == false)
                    toRemove.AddLast(obj);

            foreach (SC_SpaceObject obj in toRemove)
                RemoveObject(obj);
        }

        public void addEffect(Texture2D _texture, SC_SpaceObject _obj, int _duration)
        {
            SC_Effect sceffec = new SC_Effect(_texture);
            addObject(sceffec);

            sceffec.Activated = true;
            sceffec.Position = _obj.Position;
            sceffec.Velocity = _obj.Velocity;
            sceffec.Duration = _duration;
        }

        private void updateAllObjects(TimeSpan elapsedTime)
        {
            TimeSpan slowTimeSpan = new TimeSpan((long)(elapsedTime.TotalMilliseconds * 1000.0f));
            TimeSpan fastTimeSpan = new TimeSpan((long)(elapsedTime.Ticks * 1.00f));

            foreach (SC_SpaceObject obj in objects)
            {
                if (obj is SC_SpaceCraft || obj is SC_Rocket)
                    obj.update(elapsedTime);
                else
                    if (obj is SC_Satellite)
                        obj.update(fastTimeSpan);
                    else
                        obj.update(slowTimeSpan);   
            }
        }

        private void addEarthGravityAtAllObjects()
        {
            foreach (SC_SpaceObject obj1 in physicsObjects)
                if (!(obj1 is SC_Earth))
                {
                    if (obj1 is SC_SpaceCraft)
                        obj1.addGravityForceOf(earth, 0.01f);
                    else
                        obj1.addGravityForceOf(earth);
                }
        }

        internal SC_Earth getEarth()
        {
            return this.earth;
        }

        internal SC_Rocket getRocket()
        {
            return this.rocket;
        }

        internal LinkedList<SC_SpaceObject> getPhysicsObjects()
        {
            return this.physicsObjects;
        }
    }
}
